package utils

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"strings"
	"sync"
	"time"

	"github.com/samber/lo"
	"github.com/xuri/excelize/v2"
)

var ErrExcelHeaderNotFound = errors.New("未找到表头")

func GetExcelContent(file io.Reader, checkIsHeader func(columns []string) bool) (header []string, body [][]string, err error) {
	excel, err := excelize.OpenReader(file)
	if err != nil {
		return nil, nil, err
	}
	defer excel.Close()
	// 读取第一张表
	sheetName := excel.GetSheetName(0)
	rows, err := excel.Rows(sheetName)
	if err != nil {
		return nil, nil, err
	}
	defer rows.Close()
	body = make([][]string, 0, 10)
	for rows.Next() {
		row, err := rows.Columns()
		if err != nil {
			return nil, nil, err
		}
		if len(row) == 0 {
			continue
		}
		row = lo.Map(row, func(item string, index int) string {
			return strings.TrimSpace(item)
		})
		isEmpty := lo.EveryBy(row, func(item string) bool {
			return item == ""
		})
		if isEmpty {
			continue
		}
		body = append(body, row)
	}
	if checkIsHeader == nil {
		return nil, body, nil
	}
	for index, row := range body {
		if checkIsHeader(row) {
			header = row
			body = body[index+1:]
			break
		}
	}
	if len(header) == 0 {
		return nil, nil, ErrExcelHeaderNotFound
	}
	return header, body, nil
}
func CreateExcelContent(header []string, body [][]string) (content *bytes.Buffer, err error) {
	excel := excelize.NewFile()
	sheetName := "Sheet1"
	excel.SetSheetName("Sheet1", sheetName)
	for columnIndex, value := range header {
		cell, err := excelize.CoordinatesToCellName(columnIndex+1, 1)
		if err != nil {
			return nil, err
		}
		excel.SetCellValue(sheetName, cell, value)
	}
	for rowIndex, row := range body {
		for columnIndex, value := range row {
			cell, err := excelize.CoordinatesToCellName(columnIndex+1, rowIndex+2)
			if err != nil {
				return nil, err
			}
			excel.SetCellValue(sheetName, cell, value)
		}
	}
	return excel.WriteToBuffer()
}

type SheetsData struct {
	Header    []string
	Body      [][]string
	SheetName string
	Err       []string
}

func WriteDataToExcel(ctx context.Context, sheets []SheetsData) (content *bytes.Buffer, err error) {
	excel := excelize.NewFile()
	var wg sync.WaitGroup
	var gErr error
	wg.Add(len(sheets))

	for i, sheet := range sheets {
		go func(i int, sheet SheetsData) {
			defer func() {
				if err := recover(); err != nil {
					return
				}
				wg.Done()
			}()
			sheetIndexName := fmt.Sprintf("Sheet%d", i+1)
			if _, err := excel.NewSheet(sheetIndexName); err != nil {

				return
			}
			if err := excel.SetSheetName(sheetIndexName, sheet.SheetName); err != nil {

			}
			excel.SetActiveSheet(i + 1)

			for columnIndex, value := range sheet.Header {
				cell, err := excelize.CoordinatesToCellName(columnIndex+1, 1)
				if err != nil {
					gErr = err
					return
				}
				excel.SetCellValue(sheet.SheetName, cell, value)
			}
			for rowIndex, row := range sheet.Body {
				for columnIndex, value := range row {
					cell, err := excelize.CoordinatesToCellName(columnIndex+1, rowIndex+2)
					if err != nil {
						gErr = err
					}
					excel.SetCellValue(sheet.SheetName, cell, value)
				}
			}
		}(i, sheet)
	}
	wg.Wait()
	if gErr != nil {
		return nil, gErr
	}
	return excel.WriteToBuffer()
}

const (
	SheetNameGoodsAttr = "商品参数"
	//  SheetNameGoodsKnowledge = "商品知识"   TODO 2.1版本暂不支持导入商品知识
)

func ReadDataFromExcel(file io.Reader, headers map[string][]string) (data []*SheetsData, err error) {
	excel, err := excelize.OpenReader(file)
	if err != nil {
		return nil, err
	}
	defer excel.Close()

	sheetDatas := make([]*SheetsData, 0, 2)

	sheetNames := excel.GetSheetList()
	for _, sheetName := range sheetNames {
		rows, err := excel.Rows(sheetName)
		if err != nil {
			return nil, err
		}
		defer rows.Close()
		body := make([][]string, 0, 10)
		for rows.Next() {
			row, err := rows.Columns()
			if err != nil {
				return nil, err
			}
			if len(row) == 0 {
				continue
			}
			row = lo.Map(row, func(item string, index int) string {
				return strings.TrimSpace(item)
			})
			isEmpty := lo.EveryBy(row, func(item string) bool {
				return item == ""
			})
			if isEmpty {
				continue
			}
			// 检测时间格式
			for index, item := range row {
				if len(item) == 0 {
					continue
				}
				timeType := 0
				dateFormats := []string{"2006年1月2日", "2006/1/2"}
				var parsedTime time.Time
				for t, format := range dateFormats {
					parsedTime, err = time.Parse(format, item)
					if err == nil {
						timeType = t
						break
					}
				}
				if err == nil {
					if timeType > 0 && timeType < len(dateFormats) {
						row[index] = parsedTime.Format(dateFormats[timeType])
					}
				}
			}

			body = append(body, row)
		}

		sheetData := SheetsData{SheetName: sheetName}
		for index, row := range body {
			if headers[sheetName] == nil {
				break
			}
			if lo.Every(row, headers[sheetName]) {
				sheetData.Header = row
				sheetData.Body = body[index+1:]
				break
			}
		}
		if len(sheetData.Header) == 0 {
			return nil, ErrExcelHeaderNotFound
		}
		sheetDatas = append(sheetDatas, &sheetData)
	}

	return sheetDatas, nil
}

// SplitStringByMultiCharacter split string by multi character，并且去除空格
// example:
// SplitStringByMultiCharacter(" a , b、 c，d ", ",、，") => ["a", "b", "c", "d"]
func SplitStringByMultiCharacter(s string, sep string) []string {
	if s == "" {
		return []string{}
	}
	res := strings.FieldsFunc(s, func(r rune) bool {
		return strings.Contains(sep, string(r))
	})
	res = lo.Map(res, func(item string, index int) string {
		return strings.TrimSpace(item)
	})
	res = lo.Filter(res, func(item string, index int) bool {
		return item != ""
	})
	return res
}
